package org.jacp.demo.ui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JPanel;

import org.jacp.api.action.IAction;
import org.jacp.api.action.IActionListener;
import org.jacp.api.componentLayout.Layout;
import org.jacp.demo.globals.GlobalSettings;
import org.jacp.demo.util.DropShadowPanel;
import org.jacp.demo.util.FolderOpenAction;
import org.jacp.swing.rcp.component.ASwingComponent;
import org.jdesktop.swingx.JXBusyLabel;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.jdesktop.swingx.util.OS;

public class ImageEditor extends ASwingComponent {

	private final List<JPanel> images = new CopyOnWriteArrayList<JPanel>();

	private JButton right;

	private JButton left;

	private int maxAmount = 1;

	private Dimension initialDimension;

	private JPanel pane;

	private final AtomicInteger counter = new AtomicInteger(0);

	private final AtomicInteger seeker = new AtomicInteger(0);
	
	private JXBusyLabel label;

	private final Map<Integer, Integer> refreshed = new ConcurrentHashMap<Integer, Integer>();

	/**
	 * Create the panel.
	 */
	public ImageEditor() {

	}

	@Override
	public void handleBarEntries(final Map<Layout, Container> bars) {
		final Container toolBar = bars.get(Layout.SOUTH);
		toolBar.add(getImportDialogButton());
		toolBar.add(getBackButton());
		toolBar.add(getNextButton());
		toolBar.add(createBusyLabel());

	}
	
	private final JXBusyLabel createBusyLabel(){
		  JXBusyLabel label = new JXBusyLabel();
		  label.setToolTipText("import images");
		  label.setEnabled(false);
		  label.setBusy(false);
		  label.setVisible(false);
		  this.label = label;
		  return label;
		}
	
	private final void enableBusyLabel() {
		this.label.setEnabled(true);
		this.label.setBusy(true);
		this.label.setVisible(true);
	}
	private final void disableBusiyLabel() {
		this.label.setEnabled(false);
		this.label.setBusy(false);
		this.label.setVisible(false);
	}

	@Override
	public void handleMenuEntries(final Container arg0) {

	}

	@SuppressWarnings("unchecked")
	@Override
	public Container handleAction(final IAction<ActionEvent, Object> action) {

		if (pane == null && action.getLastMessage() instanceof Dimension) {
			// init editor
			initUI((Dimension) action.getLastMessage());
		} else if (action.getLastMessage() instanceof List<?>) {
			// add a list of Thumbnails to page
			addThumbnailsToPage((List<DropShadowPanel>) action.getLastMessage());
		} else if (action.getLastMessage() instanceof DropShadowPanel) {
			// add a single Thumbnail
			addThumbnail((DropShadowPanel) action.getLastMessage());
		} else if (action.getLastMessage() instanceof Integer) {
			// scroll through image pages
			pane = images.get((Integer) action.getLastMessage());
		} else if(action.getLastMessage() instanceof String) {
			String message = (String) action.getLastMessage();
			if(message.equals("busy")) {
				enableBusyLabel();
			} else {
				disableBusiyLabel();
			}
		}

		handlePanelLoad();
		handlePanelUnload();
		return pane;
	}

	/**
	 * clear DropShadowPanels in not visible panels to clear memory!!
	 */
	private final void handlePanelUnload() {
		// get current panel
		final int backwardPrecached = seeker.intValue() - 2;
		final int forwardPrecached = seeker.intValue() + 2;
		if (refreshed.containsKey(backwardPrecached)) {
			final JPanel tmp = images.get(backwardPrecached);
			unloadPanels(tmp);
			refreshed.remove(backwardPrecached);
		}
		if (refreshed.containsKey(forwardPrecached)) {
			final JPanel tmp = images.get(forwardPrecached);
			unloadPanels(tmp);
			refreshed.remove(forwardPrecached);
		}
	}

	/**
	 * updates DropShadowPanel with concrete thumbnail when needed
	 */
	private final void handlePanelLoad() {
		// get current panel
		final JPanel tmp = images.get(seeker.intValue());
		// was panel already cached or not?
		if (!refreshed.containsKey(seeker.intValue())) {
			// DropShadowPanels in pane were not loaded yet
			final int precache = seeker.intValue();
			loadPanels(1, precache);

		} else if (refreshed.containsKey(seeker.intValue())) {
			// DropShadowPanels in pane were loaded; check if DropShadowPanel count is correct in pane;  get next panel and check
			// the preload state
			final int precache = seeker.intValue() + 1;
			final int cached = refreshed.get(seeker.intValue());
			if (tmp.getComponentCount() != cached) {
				loadPanels(1, seeker.intValue());
			}
			if (!refreshed.containsKey(precache)) {
				loadPanels(1, precache);

			}
		}
	}

	private final void unloadPanels(final JPanel panelToClear) {
		if (panelToClear != null && panelToClear.getComponents().length > 0) {
			final Component[] componentsToRefresh = panelToClear
					.getComponents();
			for (int i = 0; i < componentsToRefresh.length; i++) {
				final Component comp = componentsToRefresh[i];
				if (comp instanceof DropShadowPanel) {
					((DropShadowPanel) comp).clear();
				}
			}
		}
	}

	/**
	 * load concrete thumbnails for panel position "precache" and preload panels
	 * of amount "precachePages"
	 * 
	 * @param precachePages
	 * @param precache
	 */
	private final void loadPanels(final int precachePages, final int precache) {
		for (int m = 0; m <= precachePages; m++) {
			// get position for current panel to pre load
			final int pos = precache + m;
			// check if panel at that position exists
			if (images.size() > pos) {
				final JPanel tmpPane = images.get(pos);
				if (tmpPane != null && tmpPane.getComponents().length > 0) {
					final Component[] componentsToRefresh = tmpPane
							.getComponents();
					final List<DropShadowPanel> panels = new ArrayList<DropShadowPanel>();
					// collect all components where no thumbnail is set
					for (int i = 0; i < componentsToRefresh.length; i++) {
						final Component comp = componentsToRefresh[i];
						if (comp instanceof DropShadowPanel
								&& !((DropShadowPanel) comp).isThumbnailSet()) {
							panels.add((DropShadowPanel) comp);
						}
					}
					// if components to pre load exists send message to
					// LoadImageService
					if (!panels.isEmpty()) {
						final IActionListener<ActionListener, ActionEvent, Object> listener = getActionListener();
						listener.getAction().addMessage("id01.id06", panels);
						listener.notifyComponents(listener.getAction());
						refreshed.put(pos, componentsToRefresh.length);
					}

				}
			}

		}
	}

	private final void addThumbnail(final DropShadowPanel panel) {
		final JPanel pane = getPanel();
		pane.add(panel);
		pane.invalidate();
		pane.validate();
		counter.incrementAndGet();
	}

	private final void addThumbnailsToPage(final List<DropShadowPanel> thubnails) {
		for (int i = 0; i < thubnails.size(); i++) {
			final DropShadowPanel p = thubnails.get(i);
			addActionToPanel(p);
			final JPanel myPane = getPanel();
			myPane.add(p);
			counter.incrementAndGet();
		}
		final JPanel current = images.get(seeker.intValue());
		if(current!=null) {
			current.invalidate();
			current.validate();
		}

	}

	private final void addActionToPanel(final DropShadowPanel p) {
		p.setListener(getActionListener());
		p.setTarget("id01.id08");
	}

	/**
	 * create first pane and calculate max amount of DropShadowPanels per pane
	 * 
	 * @param d
	 */
	private final void initUI(final Dimension d) {
		initialDimension = d;
		pane = new JPanel(new FlowLayout());
		pane.setBackground(new Color(0x666666));
		pane.setPreferredSize(new Dimension(initialDimension.width,
				initialDimension.height + (initialDimension.height / 2)));

		images.add(pane);
		calculateMaximumAmount();
	}

	/**
	 * calculate maximum amount of DropShadowPanels per pane
	 */
	private void calculateMaximumAmount() {
		int wMax = GlobalSettings.SHADOW_PANEL_WIDTH + 5;
		wMax = initialDimension.width / wMax;
		int hMax = GlobalSettings.SHADOW_PANEL_HEIGHT + 5;
		hMax = (initialDimension.height + (initialDimension.height / 2)) / hMax;
		maxAmount = wMax * hMax;
	}

	/**
	 * is maximum amount of DropSahdowPanels in first panel reached?
	 * 
	 * @return
	 */
	private final boolean isFirstPanel() {
		final int count = counter.intValue() > 1 ? counter.intValue() : 1;
		return count < maxAmount;
	}

	/**
	 * get current panel; each panel can hold only a specific amount of
	 * children; if amount reached maximum a new empty panel will be returned
	 * 
	 * @return
	 */
	private final JPanel getPanel() {
		final int count = counter.intValue() > 1 ? counter.intValue() : 1;
		// are more DropShadowPanels exists than allowed in one (first) panel
		if (!isFirstPanel()) {
			// how many panels are needed?
			final int panel = Math.abs(count / maxAmount);
			if ((images.size() - 1) < panel) {
				// no panels; create one
				return switchPane();
			}
			return images.get(panel);
		}
		return pane;
	}

	/**
	 * create new pane if max amount of DropShadowPanels is reached in pane
	 * 
	 * @return pane 
	 */
	private final JPanel switchPane() {
		if (counter.intValue() > maxAmount) {
			final JPanel pane = new JPanel(new FlowLayout());
			pane.setBackground(new Color(0x666666));
			pane.setPreferredSize(new Dimension(initialDimension.width,
					initialDimension.height + (initialDimension.height / 2)));
			right.setEnabled(true);
			left.setVisible(true);
			images.add(pane);
			return pane;
		}
		return pane;
	}

	private String getFile() {
		if (OS.isMacOSX()) {
			System.setProperty("apple.awt.fileDialogForDirectories", "true");
			final FileDialog fileDialog = new FileDialog(new Frame(),
					"Load Images");
			fileDialog.setVisible(true);
			final String selectedFile = fileDialog.getDirectory()
					+ fileDialog.getFile();
			if (selectedFile.contains("null")) {
				return null;
			}
			return selectedFile;
		}

		final JFileChooser chooser = new JFileChooser();
		chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
		final int returnVal = chooser.showOpenDialog(null);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			return chooser.getSelectedFile().getAbsolutePath();

		}

		return null;
	}

	private final JButton getNextButton() {
		final BufferedImage myNexgtPicture = getImageByName("images/Play.png");
		right = new JButton(new ImageIcon(GraphicsUtilities.createThumbnail(
				myNexgtPicture, 19,19)));
		right.putClientProperty("JButton.segmentPosition", "first");
		//right.putClientProperty("JButton.buttonType", "segmentedTextured");
		right.setFocusable(false);
		right.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(final ActionEvent e) {
				left.setEnabled(true);
				final int count = seeker.incrementAndGet();
				final IActionListener<ActionListener, ActionEvent, Object> listener = getActionListener();
				listener.getAction().setMessage(count);
				listener.notifyComponents(listener.getAction());
				if ((count + 1) == images.size()) {
					right.setEnabled(false);
				}
			}
		});
		right.setEnabled(false);
		return right;
	}

	private final JButton getBackButton() {

		final BufferedImage myBackPicture = getImageByName("images/Back.png");
		left = new JButton(new ImageIcon(GraphicsUtilities.createThumbnail(
				myBackPicture, 19, 19)));
		left.putClientProperty("JButton.segmentPosition", "first");
		//left.putClientProperty("JButton.buttonType", "segmentedTextured");
		left.setFocusable(false);
		left.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(final ActionEvent e) {
				right.setEnabled(true);
				final int count = seeker.decrementAndGet();
				final IActionListener<ActionListener, ActionEvent, Object> listener = getActionListener();
				listener.getAction().setMessage(count);
				listener.notifyComponents(listener.getAction());
				if (count == 0) {
					left.setEnabled(false);
				}
			}
		});
		left.setEnabled(false);
		left.setVisible(false);

		return left;
	}

	private final JButton getImportDialogButton() {
		final BufferedImage myPicture = getImageByName("images/Nuke.png");
		final JButton add = new JButton(new ImageIcon(GraphicsUtilities
				.createThumbnail(myPicture, 19, 19)));
		add.putClientProperty("JButton.segmentPosition", "first");
		//add.putClientProperty("JButton.buttonType", "segmentedTextured");
		add.setFocusable(false);
		add.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(final ActionEvent e) {

				final IActionListener<ActionListener, ActionEvent, Object> listener = getActionListener();
				final String file = getFile();
				if (file != null) {
					listener.getAction().addMessage(
							"id01.id05",
							new org.jacp.demo.util.FolderOpenAction(file,
									FolderOpenAction.ActionType.SCAN, true));
					listener.notifyComponents(listener.getAction());
				}
			}
		});
		return add;
	}

	private final BufferedImage getImageByName(final String name) {
		BufferedImage image = null;
		try {
			image = ImageIO.read(new File(name));
		} catch (final IOException e) {
			e.printStackTrace();
		}
		return image;
	}

}
